﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using ASPNETPerformance.App_BusinessLogic.Helpers;
using ASPNETPerformance.App_BusinessLogic.Utils;

namespace ASPNETPerformance.App_BusinessLogic.SEO {
    public class SEOHelper {
        #region BlogEngine
        /// <summary>
        /// Adds the content-type meta tag to the header.
        /// </summary>
        public static void AddMetaContentType( Page page ) {
            HtmlMeta meta = new HtmlMeta();
            meta.HttpEquiv = "content-type";
            meta.Content = HttpContext.Current.Response.ContentType + "; charset=" + HttpContext.Current.Response.ContentEncoding.HeaderName;
            page.Header.Controls.Add( meta );
        }

        /// <summary>
        /// Add a meta tag to the page's header.
        /// </summary>
        public static void AddMetaTag( Page page, string name, string value ) {
            if( string.IsNullOrEmpty( name ) || string.IsNullOrEmpty( value ) )
                return;

            HtmlMeta meta = new HtmlMeta();
            meta.Name = name;
            meta.Content = value;
            page.Header.Controls.Add( meta );
        }

        /// <summary>
        /// Adds the generic link to the header.
        /// </summary>
        public static void AddGenericLink( Page page, string relation, string title, string href ) {
            HtmlLink link = new HtmlLink();
            link.Attributes[ "rel" ] = relation;
            link.Attributes[ "title" ] = title;
            link.Attributes[ "href" ] = href;
            page.Header.Controls.Add( link );
        }

        /// <summary>
        /// Adds the generic link to the header.
        /// </summary>
        public static void AddGenericLink( Page page, string type, string relation, string title, string href ) {
            HtmlLink link = new HtmlLink();
            link.Attributes[ "type" ] = type;
            link.Attributes[ "rel" ] = relation;
            link.Attributes[ "title" ] = title;
            link.Attributes[ "href" ] = href;
            page.Header.Controls.Add( link );
        }

        /// <summary>
        /// Adds a JavaScript reference to the HTML head tag.
        /// </summary>
        public static void AddJavaScriptInclude( Page page, string url ) {
            HtmlGenericControl script = new HtmlGenericControl( "script" );
            script.Attributes[ "type" ] = "text/javascript";
            script.Attributes[ "src" ] = ResolveScriptUrl( url );
            page.Header.Controls.Add( script );
        }

        /// <summary>
        /// Adds a Stylesheet reference to the HTML head tag.
        /// </summary>
        /// <param name="url">The relative URL.</param>
        public static void AddStylesheetInclude( Page page, string url ) {
            HtmlLink link = new HtmlLink();
            link.Attributes[ "type" ] = "text/css";
            link.Attributes[ "href" ] = ResolveCSSUrl( url );
            link.Attributes[ "rel" ] = "stylesheet";
            page.Header.Controls.Add( link );
        }

        /// <summary>
        /// Get a Image reference to the HTML.
        /// </summary>
        /// <param name="url">The relative URL.</param>
        public static string GetImageUrl( string url ) {
            return ResolveImageUrl( url );
        }

        public static void AddCdnJqueryInclude( Page page ) {
            AddCdnJqueryInclude( page, true );
        }

        public static void AddCdnJqueryInclude( Page page, bool isMicrosoftOrGoogle = true ) {
            HtmlGenericControl script = new HtmlGenericControl( "script" );
            script.Attributes[ "type" ] = "text/javascript";
            script.Attributes[ "src" ] = isMicrosoftOrGoogle ? "http://ajax.microsoft.com/ajax/jquery/jquery-1.4.4.min.js" :
                "http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js";
            page.Header.Controls.Add( script );
        }

        /// <summary>
        /// Resolves the script URL.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <returns></returns>
        public static string ResolveScriptUrl( string url ) {
            if( UseCdnServer1 ) {
                return CdnServerHostAddress1( url );
            }

            if( CommonHelper.GetCookieBool( "IsStaticCacheByJsAndCss" ) ) {
                url = GetVersionedName( url );
            }
            else {
                url = MapResolvePath( url );
            }

            return CommonHelper.GetDemosLocation() + url;

        }

        public static string ResolveCSSUrl( string url ) {
            if( UseCdnServer1 ) {
                return CdnServerHostAddress1( url );
            }

            if( CommonHelper.GetCookieBool( "IsStaticCacheByJsAndCss" ) ) {
                url = GetVersionedName( url );
            }
            else {
                url = MapResolvePath( url );
            }
            return CommonHelper.GetDemosLocation() + url;

        }

        public static string ResolveImageUrl( string url ) {
            if( CommonHelper.GetCookieBool( "IsStaticCacheByImage" ) ) {
                url = GetVersionedName( url );
            }
            else {
                url = MapResolvePath( url );
            }
            return CommonHelper.GetDemosLocation() + url;
        }

        private static string MapResolvePath( string url ) {
            if( url.StartsWith( "~/" ) ) {
                url = url.Substring( 2 );
            }
            else if( url.StartsWith( "/" ) ) {
                url = url.Substring( 1 );
            }
            url.Replace( "\\", "/" );

            return url;
        }

        private static bool UseCdnServer1 {
            get {
                return ( CommonHelper.GetCookieBool( "IsEnableCdnServer1" ) &&
                        !string.IsNullOrEmpty( CommonHelper.GetCookieString( "CdnServerHost1", true ) ) );
            }
        }

        private static string CdnServerHostAddress1( string url ) {
            url = MapResolvePath( url );
            return CommonHelper.GetCookieString( "CdnServerHost1", true ) + url;
        }

        /// <summary>
        /// Adds code to the HTML head section.
        /// </summary>
        public static void AddCustomCodeToHead( Page page, string customCode ) {
            string code = string.Format( CultureInfo.InvariantCulture,
                "{0}<!-- Start custom code -->{0}{1}{0}<!-- End custom code -->{0}", Environment.NewLine, customCode );
            LiteralControl control = new LiteralControl( code );
            page.Header.Controls.Add( control );
        }

        private static string GetVersionedName( string name ) {
            if( name.StartsWith( "/" ) ) {
                name = "~" + name;
            }

            string filePath = HttpContext.Current.Server.MapPath( name );

            name = MapResolvePath( name );

            return string.Concat( name, '?', LastUpdateTimeHex( filePath ) );
        }

        public static string LastUpdateTimeHex( string path ) {
            if( path.StartsWith( "data:image" ) ) {
                return "";
            }

            // File.GetLastWriteTime returns 1/01/1601 11:00:00 AM
            // if the file doesn't exist. That corresponds with these ticks:
            const long ticksFileNotExists = 504911628000000000;

            // Cache key prefix. Used when caching the last modified time to 
            // distinguish last modified time cache entry for a file fron any
            // other cache entries for the file.
            const string cacheKeyPrefix = "lmt_";

            // ------------
            // Try to get last modified time from cache

            string cacheKey = cacheKeyPrefix + path;
            string lastUpdateTimeHex = ( string )HttpContext.Current.Cache[ cacheKey ];

            if( lastUpdateTimeHex == null ) {
                lastUpdateTimeHex = "";
                string filePath = path;
                if( filePath != null ) {
                    // Get last update time in ticks. 
                    DateTime lastUpdateTime = File.GetLastWriteTime( filePath );
                    long lastUpdateTimeTicks = lastUpdateTime.Ticks;

                    if( lastUpdateTimeTicks != ticksFileNotExists ) {
                        lastUpdateTimeHex = VersionId( lastUpdateTime );
                    }
                }

                // Cache the newly found last update time
                CacheDependency cd = new CacheDependency( filePath );
                HttpContext.Current.Cache.Insert( cacheKey, lastUpdateTimeHex, cd );
            }

            return lastUpdateTimeHex;
        }

        private static string VersionId( DateTime lastModifiedTime ) {
            long lastModifiedTimeTicks = lastModifiedTime.Ticks;

            // Shorted lastUpdateTimeSeconds. Make its units seconds rather than ticks
            // 1 second = 10,000,000 seconds. And mod by 40000000 - 40000000 seconds
            // is just over a year, so the same file has to survive over a year on the site
            // before it could possible get a duplicate last modified time .
            long lastModifiedTimeSeconds = ( lastModifiedTimeTicks / 10000000 ) % 40000000;
            string lastModifiedTimeHex = lastModifiedTimeSeconds.ToString( "x" );

            return lastModifiedTimeHex;
        }

        public static void SetConditionalBrowserCache( HttpContext context, DateTime date ) {
            string etag = "\"" + date.Ticks + "\"";
            string incomingEtag = context.Request.Headers[ "If-None-Match" ];

            context.Response.Cache.SetETag( "\"" + date.Ticks + "\"" );
            context.Response.Cache.SetLastModified( date );

            if( String.Compare( incomingEtag, etag ) == 0 ) {
                context.Response.Clear();
                context.Response.StatusCode = ( int )System.Net.HttpStatusCode.NotModified;
                context.Response.End();
            }
        }

        #endregion
    }
}